package com.ease.util {

   import flash.utils.Dictionary;

   /**
    * @private
    *
    */
   public class Set
   {
      private var _length:int = 0;
      private var _set:Dictionary;

      public function Set( useWeakReferences:Boolean = false ) {
         _set = new Dictionary( useWeakReferences );
      }

      public function get length():int {
         return _length;
      }

      public function add( item:* ):void {
         if (! _set[item]) {
            _length++;
         } else {
            return;
         }
         _set[item] = true;
      }

      public function addCollection( items:Array ):void {
         if (! items) {
            return;
         }
         for each (var item:Object in items) {
            add( item );
         }
      }

      public function addSet( set:Set ):void {
         set.forEach(
            function( item:* ):void {
               add( item );
            });
      }

      public function remove( item:* ):void {
         if (_set[item]) {
            _length--;
         } else {
            return;
         }
         delete _set[item];
      }

      public function contains( item:* ):Boolean {
         return _set[item];
      }

      /**
       * Executes a test function on each item in the set and removes items that
       * return <code>false</code> for the test function.
       *
       * @param job
       *    The function to execute, in form of:
       *    <code>function job(item:*):Boolean;</code>
       */
      public function filter( job:Function ):void {
         for (var item:* in _set) {
            if (!job( item )) {
               remove( item );
            }
         }
      }

      /**
       * Invokes the given 'job' function for each item in this set one by one, treating the
       * item as the argument to the method. If the optional arg is provided, it serves as
       * the second argument to the invoked method. The return value contains the values
       * returned by the job, keyed on the items in the set.
       */
      public function forEach( job:Function, arg:* = null ):Dictionary {
         var returnValueByItem:Dictionary = new Dictionary();
         var returnVal:Object;
         if (arg) {
            for (var item:* in _set) {
               returnVal = job( item, arg );
               returnValueByItem[item] = returnVal;
            }
         } else {
            for (item in _set) {
               returnVal = job( item );
               returnValueByItem[item] = returnVal;
            }
         }
         return returnValueByItem;
      }

      /**
       * Returns an item that does not satisfy the given test function if any such item is
       * found in the set. Otherwise returns null.
       */
      public function every( test:Function, arg:* = null, breakOn:Boolean = false ):* {
         if (arg) {
            for (var item:* in _set) {
               if (test( item, arg ) == breakOn) {
                  return item;
               }
            }
         } else {
            for (item in _set) {
               if (test( item ) == breakOn) {
                  return item;
               }
            }
         }
      }

      /**
       * Returns an array containing all of the elements in this set
       */
      public function toArray():Array {
         return MixedUtil.getKeys(_set);
      }

      public static function fromArray( array:Array ):Set {
         var instance:Set = new Set();
         for each (var item:* in array) {
            instance.add( item );
         }
         return instance;
      }

      protected function get set():Dictionary {
         return _set;
      }

      protected function setLength(len:int):void {
         _length = len;
      }
   } // class

}